/**
 * 统一(多格式)模型加载器
 *
 * 功能
 *   多格式支持：GLTF、FBX
 *   为模型套一层 Group，为了防止模型根节点有变换而影响后期修改变换
 *
 * 方法
 *   .load(url, onLoad, onProgress, onError, fixUpAxis)
 *     onLoad(model)
 *     onProgress - 同 three.js
 *     onError - 同 three.js
 *     fixUpAxis - (仅对 FBX 格式有效)是否修正坐标轴旋转问题
 */
import * as THREE from "three";
import { material, angle } from "./utils";

import {
  GLTFLoader,
  type GLTF,
} from "three/examples/jsm/loaders/GLTFLoader.js";
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader.js";
import { FBXLoader } from "three/examples/jsm/loaders/FBXLoader.js";

const FIX_UP_AXIS_ANGLE = angle.degToRad(-90);

const gltfLoader = new GLTFLoader();
const dracoLoader = new DRACOLoader();
const fbxLoader = new FBXLoader();

dracoLoader.setDecoderPath("/lib/draco/");
gltfLoader.setDRACOLoader(dracoLoader);

export class UniformModelLoader {
  load(
    url: string,
    onLoad?: (object: THREE.Object3D) => void,
    onProgress?: (event: ProgressEvent<EventTarget>) => void,
    onError?: (error: unknown) => void,
    fixUpAxis = false
  ): Promise<THREE.Object3D> {
    return new Promise((resolve) => {
      const lowerUrl = url.toLowerCase();
      const isDataUrl = lowerUrl.startsWith("data:");
      const isFBX = lowerUrl.endsWith(".fbx");
      const isGLTF = lowerUrl.endsWith(".glb") || lowerUrl.endsWith(".gltf");
      if (isGLTF || isDataUrl) {
        const onGLTFLoad = (gltf: GLTF) => {
          const object = gltf.scene;
          const group = new THREE.Group();
          group.add(object);
          group.animations.push(...gltf.animations);
          material.fixFbxToGlbTransparent(group);
          onLoad?.(group);
          resolve(group);
        };
        gltfLoader.load.call(gltfLoader, url, onGLTFLoad, onProgress, onError);
      } else if (isFBX) {
        const onFBXLoad = (model: THREE.Object3D) => {
          if (fixUpAxis) {
            model.rotation.x = FIX_UP_AXIS_ANGLE;
            const group = new THREE.Group();
            group.add(model);
            onLoad?.(group);
            resolve(group);
          } else {
            onLoad?.(model);
            resolve(model);
          }
        };
        fbxLoader.load.call(fbxLoader, url, onFBXLoad, onProgress, onError);
      } else {
        alert("[UniformModelLoader] 不支持此模型格式");
      }
    });
  }
}
